package main

import (
	"fmt"
	"os"
	"strconv"
	"strings"

	"github.com/kpango/glg"
	"github.com/valyala/fasthttp"
)

type Server struct {
	controller Controller
}

func (srv *Server) Setup() {
	srv.controller = Controller{
		basepath: *basepath,
		dbname:   *dbname,
		logname:  *logfile,
	}
	if err := srv.controller.Setup(); err != nil {
		glg.Errorf("Fail to set up controller: %v", err)
		os.Exit(1)
	}
}

func (srv *Server) Close() {
	err := srv.controller.Close()
	if err != nil {
		glg.Errorf("Fail to close server: %v", err)
	}
}

func (srv *Server) Handle(ctx *fasthttp.RequestCtx) {
	ret := NewResult()
	data := parseArgs(ctx)
	if ctx.IsGet() {
		ret.WithAction(GET)
		if v, err := srv.get(data); err == nil {
			ret.WithValue(v).WithCode(fasthttp.StatusOK)
		} else {
			ret.WithError(err)
		}
	} else if ctx.IsPost() {
		ret.WithAction(POST)
		if err := srv.put(data); err == nil {
			ret.WithCode(fasthttp.StatusOK)
		} else {
			ret.WithError(err)
		}
	} else {
		ret.WithError(ActionNotSupportError)
	}

	if ret.err != nil {
		if ret.err == Status404NotFoundErr {
			ret.WithCode(fasthttp.StatusNotFound)
		} else if isParamError(ret.err) {
			msg := "Request args or path error. ip: %s , action: %s , path: %s , args: %s\n"
			glg.Errorf(msg, data["client"], data["action"], data["path"], data["args"])
			ret.WithCode(fasthttp.StatusBadRequest)
		} else {
			ret.WithCode(fasthttp.StatusInternalServerError)
		}
	}

	if ret.code == fasthttp.StatusOK {
		msg := "Response success. ip: %s , action: %s , path: %s , args: %s\n"
		glg.Infof(msg, data["client"], data["action"], data["path"], data["args"])
		ctx.SuccessString("application/json", ret.String())
	} else {
		msg := "Response error. ip: %s , action: %s, path: %s , args: %s\n"
		glg.Infof(msg, data["client"], data["action"], data["path"], data["args"])
		ctx.Error(string(ret.bytes()), ret.code)
	}
}

func parseArgs(ctx *fasthttp.RequestCtx) (data map[string]string) {
	data = make(map[string]string)
	data["path"] = string(ctx.Path())
	data["action"] = ""
	data["args"] = ""
	data["client"] = getRealAddr(ctx)
	if ctx.IsGet() {
		data["addr"] = string(ctx.QueryArgs().Peek("addr"))
		data["source"] = string(ctx.QueryArgs().Peek("source"))
		data["vercmp"] = string(ctx.QueryArgs().Peek("vercmp"))
		data["state"] = string(ctx.QueryArgs().Peek("state"))
		data["ver"] = string(ctx.QueryArgs().Peek("ver"))
		data["type"] = string(ctx.QueryArgs().Peek("type"))
		data["time"] = string(ctx.QueryArgs().Peek("time"))
		data["return"] = string(ctx.QueryArgs().Peek("return"))
		data["interval"] = string(ctx.QueryArgs().Peek("interval"))
		data["action"] = "GET"
		data["args"] = ctx.QueryArgs().String()
	} else if ctx.IsPost() {
		data["addr"] = getRealAddr(ctx)
		data["ip"] = string(ctx.PostArgs().Peek("ip"))
		data["remove"] = string(ctx.PostArgs().Peek("remove"))
		path := string(ctx.Path())
		data["source"] = strings.Split(path, "/")[1]
		data["state"] = string(ctx.PostArgs().Peek("state"))
		data["action"] = "POST"
		data["args"] = ctx.PostArgs().String()
	}
	msg := "Receive %s request from ip: %s,  Request path: %s, Request args: %s\n"
	glg.Infof(msg, data["action"], data["client"], data["path"], data["args"])
	return
}

func getRealAddr(ctx *fasthttp.RequestCtx) string {
	var addr []byte
	if addr = ctx.Request.Header.Peek("Clientip"); addr != nil {
		return strings.Split(string(addr), ",")[0]
	} else {
		return ctx.RemoteIP().String()
	}
}

func (srv *Server) put(data map[string]string) error {
	if data["remove"] != "" && data["ip"] != "" {
		return srv.controller.Remove(data["ip"], data["remove"])
	} else if data["addr"] != "" && data["source"] != "" && data["state"] != "" {
		srv.controller.BufferWrite(data)
		return nil
	} else {
		return ReqParametersError
	}
}

func (srv *Server) get(data map[string]string) (string, error) {
	switch data["path"] {
	case "/", "":
		if data["interval"] != "" {
			interval, err := strconv.Atoi(data["interval"])
			if err != nil {
				return "", ReqParametersError
			}
			srv.controller.BatchPut()
			return srv.controller.GetStatesCount(interval)
		} else if data["ver"] == "" && data["vercmp"] == "" && data["type"] == "" && data["time"] == "" {
			return srv.controller.GetAddrs()
		} else {
			return srv.controller.GetAddrsLimited(data["ver"], data["vercmp"], data["type"], data["time"])
		}
	case "/addr", "/addr/":
		if data["addr"] != "" {
			return srv.controller.GetSources(data["addr"])
		} else {
			return "", ReqParametersError
		}
	case "/addr/source", "/addr/source/":
		if data["addr"] != "" && data["source"] != "" && data["return"] != "" {
			if data["return"] == "state" {
				return srv.controller.GetStatesArr(data["addr"], data["source"])
			} else if data["return"] == "version" {
				return srv.controller.GetVersionsMap(data["addr"], data["source"])
			} else {
				return "", ReqParametersError
			}
		} else {
			return "", ReqParametersError
		}
	case "/addr/source/state", "/addr/source/state/":
		if data["source"] == "" {
			data["source"] = ".*"
		}
		if data["addr"] != "" && data["source"] != "" && data["state"] != "" {
			return srv.controller.GetTimes(data["addr"], data["source"], data["state"])
		} else {
			return "", ReqParametersError
		}
	default:
		return "", RequestPathError
	}
}

func isParamError(err error) bool {
	return err == ActionNotSupportError || err == RequestPathError ||
		err == ReqParametersError
}

var ActionNotSupportError = fmt.Errorf("ActionNotSupport")
var RequestPathError = fmt.Errorf("RequestPathError")
var ReqParametersError = fmt.Errorf("ReqParametersError")
var Status404NotFoundErr = fmt.Errorf("404NotFound")
var BatchPutErr = fmt.Errorf("BatchPutError")
